/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "rdx_pragmas.hpp"
#include "rdx_basictypes.hpp"

namespace RDX
{
	namespace Programmability
	{
		template<class _Tchar, class _Tint>
		inline int myatoll(const _Tchar *str, _Tint &out)
		{
			const _Tchar *baseStr = str;

			bool negative = false;
			if(str[0] == '-')
			{
				negative = true;
				str++;
			}
			_Tint rv = 0;
			while(str[0] >= '0' && str[0] <= '9')
			{
				if(negative)
					rv = static_cast<_Tint>(rv * 10 - (str[0] - '0'));
				else
					rv = static_cast<_Tint>(rv * 10 + (str[0] - '0'));
				str++;
			}
			out = rv;
			return str - baseStr;
		}
		
		template<class _Tchar, class _Tint>
		inline int IntToString(_Tchar *out, _Tint v)
		{
			_Tchar backstack[50];
			int digits = 0;
			int chars = 0;
			bool negative = false;

			if(v < 0)
			{
				*out++ = '-';
				chars++;
				negative = true;
			}

			do
			{
				if(negative)
					backstack[digits++] = static_cast<_Tchar>('0' - (v % static_cast<_Tint>(10)));
				else
					backstack[digits++] = static_cast<_Tchar>('0' + (v % static_cast<_Tint>(10)));
				v = static_cast<_Tint>(v / static_cast<_Tint>(10));
			} while(v);

			while(digits)
			{
				*out++ = backstack[--digits];
				chars++;
			}

			*out = '\0';

			return chars;
		}

		template<class _Tchar, class _Tfrac, class _Tx>
		inline int DecomposedToString(_Tchar *out, _Tfrac frac, _Tx x)
		{
			int nChars = 0;
			nChars += IntToString<_Tchar, _Tfrac>(out, frac);
			if(x)
			{
				out[nChars++] = '^';
				nChars += IntToString<_Tchar, _Tx>(out + nChars, x);
			}
			out[nChars] = static_cast<_Tchar>('\0');
			return nChars;
		}

		template<class _Tchar, class _Tfrac, class _Tx>
		inline void DecomposeString(const _Tchar *str, _Tfrac &frac, _Tx &x)
		{
			str += myatoll<_Tchar, _Tfrac>(str, frac);

			if(*str == '^')
			{
				str++;
				myatoll<_Tchar, _Tx>(str, x);
			}
			else
				x = 0;
		}

		void DecomposeFloat(Float32 f, Int32 &frac, Int32 &x)
		{
			union
			{
				Float32 f;
				Int32 i;
			} u;

			u.f = f;
			if(u.i == 0)
			{
				frac = 0;
				x = 0;
				return;
			}

			Int32 baseFrac = u.i & 0x7FFFFF;
			baseFrac |= 0x800000;
			Int32 baseX = (u.i & 0x7F800000) >> 23;

			while(!(baseFrac & 1))
			{
				baseX++;
				baseFrac >>= 1;
			}

			x = baseX - 150;

			if((u.i & 0x80000000) != 0)
				baseFrac = -baseFrac;
			frac = baseFrac;
		}

		float RecomposeFloat(Int32 frac, Int32 x)
		{
			union
			{
				Float32 f;
				Int32 i;
			} u;

			if(frac == 0)
				return 0;

			bool sign = false;
			if(frac < 0)
			{
				sign = true;
				frac = -frac;
			}

			x += 150;

			while(!(frac & 0x800000))
			{
				x--;
				frac <<= 1;
			}

			frac &= 0x7FFFFF;
			u.i = frac | (x << 23);
			if(sign)
				u.i |= 0x80000000;

			return u.f;
		}

		void DecomposeDouble(Float64 f, Int64 &frac, Int32 &x)
		{
			union
			{
				Float64 f;
				Int64 i;
			} u;

			u.f = f;
			if(u.i == 0)
			{
				frac = 0;
				x = 0;
				return;
			}

			Int64 baseFrac = u.i & 0xFFFFFFFFFFFFFLL;
			baseFrac |= 0x10000000000000LL;
			Int32 baseX = static_cast<Int32>( (u.i & 0x7FF0000000000000LL) >> 52LL);

			while(!(baseFrac & 1))
			{
				baseX++;
				baseFrac >>= 1LL;
			}

			x = baseX - 1075;

			if((u.i & 0x8000000000000000LL) != 0)
				baseFrac = -baseFrac;
			frac = baseFrac;
		}


		double RecomposeDouble(Int64 frac, Int32 x)
		{
			union
			{
				Float64 f;
				Int64 i;
			} u;

			x += 1075;

			bool sign = false;
			if(frac < 0LL)
			{
				sign = true;
				frac = -frac;
			}

			while(!(frac & 0x10000000000000LL))
			{
				x--;
				frac <<= 1LL;
			}

			frac &= 0xFFFFFFFFFFFFFLL;
			u.i = frac | (static_cast<Int64>(x) << 52LL);
			if(sign)
				u.i |= 0x8000000000000000LL;

			return u.f;
		}
		
		void DecodeString(const Char *s, Int8 &i)
		{
			myatoll<Char, Int8>(s, i);
		}
		
		void DecodeString(const Char *s, Int16 &i)
		{
			myatoll<Char, Int16>(s, i);
		}
		
		void DecodeString(const Char *s, Int32 &i)
		{
			myatoll<Char, Int32>(s, i);
		}

		void DecodeString(const Char *s, Int64 &i)
		{
			myatoll<Char, Int64>(s, i);
		}

#ifdef RDX_WCHAR_T_IS_NATIVE
		void DecodeString(const Char *s, wchar_t &i)
		{
			myatoll<Char, wchar_t>(s, i);
		}
#endif

		void DecodeString(const Char *s, UInt16 &i)
		{
			myatoll<Char, UInt16>(s, i);
		}

		void DecodeString(const Char *s, UInt8 &i)
		{
			myatoll<Char, UInt8>(s, i);
		}
		
		void DecodeString(const Char *s, UInt64 &i)
		{
			myatoll<Char, UInt64>(s, i);
		}

		void DecodeString(const Char *s, UInt32 &i)
		{
			myatoll<Char, UInt32>(s, i);
		}

		void DecodeString(const Char *s, Float32 &f)
		{
			Int32 frac,x;
			DecomposeString<Char, Int32, Int32>(s, frac, x);
			f = RecomposeFloat(frac, x);
		}

		void DecodeString(const Char *s, Float64 &f)
		{
			Int64 frac;
			Int32 x;
			DecomposeString<Char, Int64, Int32>(s, frac, x);
			f = RecomposeDouble(frac, x);
		}

		int EncodeString(Char *s, Int64 i)
		{
			return IntToString<Char, Int64>(s, i);
		}
		
		int EncodeString(Char *s, Int32 i)
		{
			return IntToString<Char, Int32>(s, i);
		}
		
		int EncodeString(Char *s, UInt16 i)
		{
			return IntToString<Char, UInt16>(s, i);
		}
		
		int EncodeString(Char *s, UInt8 i)
		{
			return IntToString<Char, UInt8>(s, i);
		}
		
		int EncodeString(Char *s, UInt32 i)
		{
			return IntToString<Char, UInt32>(s, i);
		}
		
		int EncodeString(Char *s, Float32 f)
		{
			Int32 frac,x;
			DecomposeFloat(f, frac, x);
			return DecomposedToString<Char, Int32, Int32>(s, frac, x);
		}

		int EncodeString(Char *s, Float64 f)
		{
			Int64 frac;
			Int32 x;
			DecomposeDouble(f, frac, x);
			return DecomposedToString<Char, Int64, Int32>(s, frac, x);
		}
	};
};
